<?php

namespace Managers;

class Map extends BaseManager
{
    const MAX_QUEST_WIDTH = 300;
    const MAX_QUEST_HEIGHT = 300;

    const TABLE = '\Catalog\Maps';
    protected $__table = self::TABLE;

    /**
     * Checks answer to the map.
     * @param $map_id
     * @param $answer
     * @return bool
     */
    function checkAnswer($map_id, $answer)
    {
        $res = $this->_em->getRepository($this->__table)->find($map_id);
        if ($res) {
            return strcasecmp($res->getAnswer(), trim($answer)) == 0;
        }

        return false;
    }

    function getById($id)
    {
        $res = $this->_em->createQueryBuilder()
                ->select('m')
                ->from($this->__table, 'm')
                ->where('m.id = :id')->setParameter('id', $id)
                ->getQuery()
                ->getArrayResult();
        return count($res) ? $res[0] : false;

    }


    public function getList($filters = array())
    {
        $qb = $this->_em->createQueryBuilder()
                ->select('m')
                ->from($this->__table, 'm');

        $this->applyFilters($qb, $filters);

        $res = $qb->getQuery()
                ->getArrayResult();

        $answersManager = new \Managers\Answers();
        foreach ($res as $key => $value) {
            $res[$key]['total'] = $answersManager->getTotal($value['id']);
        }
        return $res;
    }

    public function getTotal($filters = array())
    {
        $qb = $this->_em->createQueryBuilder()
                ->select('count(m.id) as total')
                ->from($this->__table, 'm');

        if (array_key_exists('start', $filters)) {
            unset($filters['start']);
        }

        if (array_key_exists('limit', $filters)) {
            unset($filters['limit']);
        }

        $this->applyFilters($qb, $filters);

        $res = $qb->getQuery()
                ->getArrayResult();
        return $res[0]['total'];
    }

    private function applyFilters(\Doctrine\Orm\QueryBuilder & $qb, $filters = array())
    {
        if (array_key_exists('dateStart', $filters) && is_array($filters['dateStart'])) {
            foreach ($filters['dateStart'] as $key => $date) {
                $qb->andWhere(sprintf("m.%s < '%s' ", $key, $date));
            }
        }

        if (array_key_exists('order', $filters) && is_array($filters['order'])) {
            foreach ($filters['order'] as $key => $order) {
                $qb->addOrderBy(sprintf('m.%s', $key), $order);
            }
        }

        if (array_key_exists('start', $filters)) {
            $qb->setFirstResult($filters['start']);
        }

        if (array_key_exists('limit', $filters)) {
            $qb->setMaxResults($filters['limit']);
        }

    }

    /**
     * Возвращает доступность карты в данный момент.
     * @param $id
     * @return array|false
     */
    function isAvailable($id)
    {
        $now = date('Y-m-d');

        $res = $this->_em->createQueryBuilder()
                ->select('m')
                ->from($this->__table, 'm')
                ->where('m.id = :id')->setParameter('id', $id)
                ->andWhere('m.dateend >= :now')
                ->andWhere('m.datestart <= :now')->setParameter('now', $now)
                ->getQuery()
                ->getArrayResult();

        return count($res) ? $res[0] : false;

    }

    public function getAvailable()
    {
        $now = date('Y-m-d');

        $res = $this->_em->createQueryBuilder()
                ->select('m')
                ->from($this->__table, 'm')
                ->andWhere('m.dateend > :now')
                ->andWhere('m.datestart <= :now')
                ->setParameter('now', $now)
                ->orderBy('m.id', 'DESC')
                ->setMaxResults(1)
                ->getQuery()
                ->getArrayResult();
        return count($res) ? $res[0] : false;
    }

    function getAvailableMaps($postal = 0)
    {
        $now = date('Y-m-d');
        $res = $this->_em->createQueryBuilder()
                ->select("m, abs(m.postal - {$postal}) distance")
                ->from($this->__table, 'm')
                ->where('m.dateend >= :now')->setParameter('now', $now)
                ->orderBy('distance', 'asc')
                ->getQuery()
                ->getArrayResult();

        return $res;

    }

    /**
     * @throws \Exception при ошибке покупки.
     * @param $map_id
     * @param $user_id
     * @return \Catalog\Orders|object
     */
    public function buy($map_id, $user_id)
    {
        $map = $this->_em->find($this->__table, $map_id);
        //Если существует карта.
        if (!$map) {
            throw new \Exception('Map does not exists');
        }

        $now = new \DateTime();
        //Если действие карты еще не закончилось.
        if ($map->getDateend() < $now) {
            throw new \Exception('This map is not available more.');
        }

        $user = $this->_em->find(Users::TABLE, $user_id);
        //Если существует пользователь.
        if (!$user) {
            throw new \Exception('User does not exists');
        }

        $order = $this->_em->getRepository(Orders::TABLE)->findOneBy(array('map' => $map_id, 'user' => $user_id));
        // Если карту не купил данный пользователь.
        if ($order) {
            throw new \Exception('Your answer is accepted.');
        }

        //Если у пользователя достаточно денег для покупки.
        if ($map->getPrice() > $user->getMoney()) {
            throw new \Exception('You do not have enough money.');
        }

        //Покупаем карту.
        $order = new \Catalog\Orders();
        $order->setDate($now);
        $order->setMap($map);
        $order->setUser($user);
        $this->_em->persist($order);

        //Забираем деньги у пользователя.
        $user->setMoney($user->getMoney() - $map->getPrice());
        $this->_em->persist($user);
        $this->_em->flush();

        $this->sendEmail($user->getEmail(), $map->getFile());

        return $order;
    }

    private function sendEmail($email, $file_url)
    {
        $base_url = \Zend_Registry::get('config')->resources->frontController->baseUrl;
        $real_url = $base_url . $file_url;
        $message = <<<_END
        Congratulations, you have successfully bought the map.
        <a href="{$real_url}">Download file</a>.

        <a href="{$base_url}">TreasureAce.ca</a>
_END;
        Mail::sendEmail($message, $email, 'Congratulations from TreasureAce.ca');
    }


    public function save(array $values)
    {
        $object = empty($values['id']) ? new \Catalog\Maps() : $this->_em->find(Map::TABLE, $values['id']);
        if (!$object) {
            throw new \Exception("Couldn't get map.");
        }

        $object->setAnswer($values['answer']);
        $object->setDateend(\DateTime::createFromFormat('Y-m-d', $values['dateend']));
        $object->setDatestart(\DateTime::createFromFormat('Y-m-d', $values['datestart']));
        $object->setDescription($values['description']);
        $object->setPostal($values['postal']);
        $object->setPrice($values['price']);
        $object->setTitle($values['title']);
        $object->setPrize($values['prize']);

        if (!empty($values['file'])) {
            $object->setFile($this->getSavedFileName($values['file']));
        }

        if (!empty($values['img'])) {
            $file = dirname(APPLICATION_PATH) . "/public/maps_source/{$values['img']}";
            Users::resize($file, self::MAX_QUEST_WIDTH, self::MAX_QUEST_WIDTH);
            $fileExt = pathinfo($file, PATHINFO_EXTENSION);
            $fileName = substr(md5(mt_rand()), 0, 8);
            $newFile = dirname(APPLICATION_PATH) . "/public/maps_source/{$fileName}.{$fileExt}";
            rename($file, $newFile);
            $url = "/maps_source/{$fileName}.{$fileExt}";
            $object->setUrl($url);
        }

        $this->_em->persist($object);
        $this->_em->flush();

        return $object;
    }

    private function getSavedFileName($uploaded_file)
    {
        $upload_dir = dirname(APPLICATION_PATH) . '/public/maps_files/';
        $file_ext = pathinfo($uploaded_file, PATHINFO_EXTENSION);

        do {
            $new_file = substr(md5(mt_rand()), 0, 8) . '.' . $file_ext;
        } while (file_exists($upload_dir . $new_file));
        rename($upload_dir . $uploaded_file, $upload_dir . $new_file);
        return "/maps_files/{$new_file}";
    }

    public function delete($id)
    {
        $entity = $this->_em->find($this->__table, $id);
        if ($entity) {

            //Удаляем зависимости
            $orders = $this->_em->getRepository(Orders::TABLE)->findBy(array('map' => $entity->getId()));
            foreach ($orders as $order) {
                $this->_em->remove($order);
            }

            $answers = $this->_em->getRepository(Answers::TABLE)->findBy(array('map' => $entity->getId()));
            foreach ($answers as $answer) {
                $this->_em->remove($answer);
            }

            $mails = $this->_em->getRepository(Mail::TABLE)->findBy(array('map' => $entity->getId()));
            foreach($mails as $mail) {
                $this->_em->remove($mail);
            }

            $this->_em->remove($entity);
            $this->_em->flush();
            return true;
        }
        return false;
    }

}
